package com.aratana.ui.fields;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;

import com.aratana.MVCUnit;
import com.aratana.annotation.BeforeAction;
import com.aratana.ui.FormBuilder;
import com.aratana.ui.InputChangeListener;
import com.aratana.ui.ValueChangeListener;
import com.aratana.ui.ViewField;
import com.aratana.util.IconManager;
import com.sun.awt.AWTUtilities;

@SuppressWarnings({ "serial", "restriction" })
public class LookupField<T, F> extends JPanel implements ViewField<T> {

	private final BasicField<F> field;

	private final Class<T> fieldClass;

	private final LookupSelector<T> selector;

	private T value;

	private final List<ValueChangeListener<T>> listeners = new ArrayList<ValueChangeListener<T>>();
	private final List<InputChangeListener> input = new ArrayList<InputChangeListener>();

	public LookupField(final Class<T> fieldClass, final BasicField<F> field, LookupSelector<T> selector) {
		super(new BorderLayout(0, 0));
		this.fieldClass = fieldClass;
		this.selector = selector;
		this.field = field;
		this.field.addInputChangeListener(new InputChangeListener() {

			@Override
			public void inputChange(ViewField<?> field) {
				fireInputChanged();
			}
		});
		add(this.field);

		this.field.addKeyListener(new KeyAdapter() {
			@Override
			public void keyReleased(final KeyEvent e) {
				switch (e.getKeyCode()) {
				case KeyEvent.VK_DELETE:
				case KeyEvent.VK_BACK_SPACE:
					setValue(null);
					break;
				}
			}
		});

		this.field.addFocusListener(new FocusAdapter() {

			@Override
			public void focusLost(FocusEvent e) {
				if (getValue() == null && !LookupField.this.field.getText().isEmpty()) {
					select();
				}
			}
		});

		(this.field).addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(final MouseEvent e) {
				if (e.getClickCount() == 2) {
					select();
				}
			}
		});

		add(new JButton(new AbstractAction(null, IconManager.getIcon("/icons/16x16/search.png")) {

			@Override
			public void actionPerformed(final ActionEvent e) {
				select();
			}
		}) {
			{
				setMargin(new Insets(0, 0, 0, 0));
				final int prefered = ((Component) LookupField.this.field).getPreferredSize().height;
				setPreferredSize(new Dimension(prefered, prefered));
			}
		}, BorderLayout.EAST);
	}

	@Override
	public synchronized void addFocusListener(final FocusListener l) {
		field.addFocusListener(l);
	}

	private void select() {
		new LookupDialog();
		// field.requestFocusInWindow();
	}

	@Override
	public void addValueChangeListener(final ValueChangeListener<T> listener) {
		listeners.add(listener);
	}

	@Override
	public void addInputChangeListener(InputChangeListener listener) {
		input.add(listener);
	}

	public boolean removeInputChangeListener(final InputChangeListener listener) {
		return input.remove(listener);
	}

	public void removeAllInputChangeListener() {
		input.clear();
	}

	protected void fireInputChanged() {
		for (InputChangeListener l : input) {
			l.inputChange(this);
		}
	}

	@Override
	public Class<T> getFieldClass() {
		return fieldClass;
	}

	@Override
	public int getMax() {
		return field.getMax();
	}

	@Override
	public int getMin() {
		return field.getMin();
	}

	@Override
	public T getValue() {
		return value;
	}

	@Override
	public boolean isObrigatory() {
		return field.isObrigatory();
	}

	@Override
	public T setValue(final T value) {
		boolean update = this.value != value;
		if (update) {
			for (final ValueChangeListener<T> l : listeners) {
				if (l.validateValue(value)) {
					update = false;
					break;
				}
			}
			if (update) {
				T oldValue = this.value;
				this.value = value;
				field.setText(value == null ? null : value.toString());
				this.field.setEditable(value == null);
				for (final ValueChangeListener<T> l : listeners) {
					l.valueChanged(value, oldValue);
				}
			}
		}
		return this.value;
	}

	@Override
	public T getStringValue(String str) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getValueString(T value) {
		// TODO Auto-generated method stub
		return null;
	}

	public final class LookupDialog extends JDialog {

		JComponent panel;

		private LookupDialog() {
			super(SwingUtilities.getWindowAncestor(LookupField.this), "");
			setDefaultCloseOperation(DISPOSE_ON_CLOSE);
			setUndecorated(true);
			getRootPane().setWindowDecorationStyle(JRootPane.NONE);
			AWTUtilities.setWindowOpaque(this, false);
			getRootPane().putClientProperty("apple.awt.draggableWindowBackground", false);

			setContentPane(new JPanel(null) {

				{
					enableEvents(MouseEvent.MOUSE_EVENT_MASK);
				}

				@Override
				protected void paintComponent(Graphics g) {
					g.setColor(BLUE);
					Point fieldPoint = field.getLocationOnScreen();
					SwingUtilities.convertPointFromScreen(fieldPoint, this);

					Point panelPoint = panel.getLocation();
					Polygon p = new Polygon();
					// top
					p.addPoint(fieldPoint.x, fieldPoint.y);
					p.addPoint(fieldPoint.x + field.getWidth(), fieldPoint.y);
					p.addPoint(panelPoint.x + panel.getWidth(), panelPoint.y);
					p.addPoint(panelPoint.x, panelPoint.y);
					g.fillPolygon(p);

					// bottom
					p = new Polygon();
					p.addPoint(fieldPoint.x, fieldPoint.y + field.getHeight());
					p.addPoint(fieldPoint.x + field.getWidth(), fieldPoint.y + field.getHeight());
					p.addPoint(panelPoint.x + panel.getWidth(), panelPoint.y + panel.getHeight());
					p.addPoint(panelPoint.x, panelPoint.y + panel.getHeight());
					g.fillPolygon(p);

					// left
					p = new Polygon();
					p.addPoint(fieldPoint.x, fieldPoint.y);
					p.addPoint(fieldPoint.x, fieldPoint.y + field.getHeight());
					p.addPoint(panelPoint.x, panelPoint.y + panel.getHeight());
					p.addPoint(panelPoint.x, panelPoint.y);
					g.fillPolygon(p);

					// right
					p = new Polygon();
					p.addPoint(fieldPoint.x + field.getWidth(), fieldPoint.y);
					p.addPoint(fieldPoint.x + field.getWidth(), fieldPoint.y + field.getHeight());
					p.addPoint(panelPoint.x + panel.getWidth(), panelPoint.y + panel.getHeight());
					p.addPoint(panelPoint.x + panel.getWidth(), panelPoint.y);
					g.fillPolygon(p);

					super.paintComponent(g);
				}

				@Override
				protected void processMouseEvent(MouseEvent e) {
					super.processMouseEvent(e);
					if (e.getID() == MouseEvent.MOUSE_PRESSED) {
						cancel();
					}
				}
			});

			FormBuilder formBuilder = new FormBuilder();
			formBuilder.label("Filtro :").alloc(2).width(200).name("filter").string("%%").name("search").action("Pesquisar").down()//
					.alloc(4).span(true).size(250, 300).name("table").table(getFieldClass(), selector.columnsSpecs()).down()//
					.label("").span().label("").action("Selecionar").name("cancel").action("Cancelar").down();
			add(panel = formBuilder.getPanel());

			TableField<T> table = formBuilder.getTable("table");

			for (T t : selector.lookup(null, null)) {
				table.addRow(t);
			}

			addWindowFocusListener(new WindowAdapter() {
				@Override
				public void windowDeactivated(WindowEvent e) {
					cancel();
				}

				@Override
				public void windowLostFocus(WindowEvent e) {
					cancel();
				}
			});

			// pack();
			// setLocationRelativeTo(LookupField.this);
			// setLocation(getX() + (getWidth()/2), getY() + (getHeight()/2));

			setBounds(GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds());

			panel.setBounds(getWidth() - 400, getHeight() - 500, 310, 400);

			MVCUnit.create("LookupDialog", this);

			setVisible(true);
		}

		private final Color BLUE = new Color(0.3f, 0.3f, 0.9f, 0.4f);

		@BeforeAction
		public void cancel() {
			dispose();
		}
	}
}
